package timeDivisionMultipleAccess;

/**
 * Vorlesung: Verteilte Systeme
 * Aufgabe  : 04
 * 
 * @author Benjamin Burchard
 * @author Tell Mueller-Pettenpohl
 * 
 */

import java.util.Arrays;

import timeDivisionMultipleAccess.exceptions.FrameIsFullException;
import timeDivisionMultipleAccess.exceptions.IllegalSlotException;

public class Channel {

	private static final int FRAME_SIZE = 20;
	private static final int SLOT_DISTANCE = 50;
	private static final int SLOT_HALF_SIZE = 25;
	public static final int FRAME_DURATION = 1000;

	private boolean[] frame;
	private long actFrameStartTime;
	private long frameShiftTime;

	public Channel() {
		this.frame = new boolean[FRAME_SIZE];
		Arrays.fill(frame, Boolean.TRUE);
		this.actFrameStartTime = 0;
		this.frameShiftTime -= FRAME_DURATION;
	}

	public boolean[] getFrame() {
		return frame;
	}

	public long getActSlotTime() {
		return this.actFrameStartTime;
	}

	/**
	 * This method is called by the Receiver and the field actSlotTime is 0 if
	 * the frame is empty. The field actSlotTime is necessary to calculate the 
	 * next beginning of the frame.
	 * 
	 * @param timestamp
	 * @param slotNumber
	 */
	public void setActSlotTime(long timestamp, byte slotNumber) {
		this.actFrameStartTime = timestamp
				- ((slotNumber * SLOT_DISTANCE) - SLOT_HALF_SIZE);
	}

	public void lockSlot(byte position) throws IllegalSlotException {
		if (position < FRAME_SIZE && position >= 0) {
			frame[position] = false;
		} else {
			throw new IllegalSlotException(this.getClass() + ": Slot is not in Frame!");
		}
	}

	public void unlockAllSlots() {
		Arrays.fill(frame, Boolean.TRUE);
	}
	
	public void unlockSlot(byte position) throws IllegalSlotException {
		if (position < FRAME_SIZE && position >= 0) {
			frame[position] = true;
		} else {
			throw new IllegalSlotException(this.getClass() + ": Slot is not in Frame!");
		}
	}

	public boolean isFrameEmpty() {
		int i = 0;
		for (; frame[i] == false && i < frame.length; i++)
			;
		return i + 1 == frame.length ? true : false;
	}

	public long calcNextSlotDelay(long actTime, byte nextFreeSlot) {
		long nextFreeSlotTime = calcNextSlotTime(nextFreeSlot);
		System.out.println("Delay: " + (FRAME_DURATION - this.actFrameStartTime + nextFreeSlotTime));
		return FRAME_DURATION + nextFreeSlotTime - this.actFrameStartTime + this.frameShiftTime;
	}

	public void shiftToNextFrame() {
		this.frameShiftTime = this.frameShiftTime + FRAME_DURATION;
	}

	public byte searchNextFreeSlot() throws FrameIsFullException {
//		this.frameShiftTime -= FRAME_DURATION;
		for (byte freeSlot = 0; freeSlot < frame.length; freeSlot++) {
			if (frame[freeSlot] == true)
				return freeSlot;
		}
		throw new FrameIsFullException("Frame is full! Wait a second...");
	}

	private long calcNextSlotTime(byte freeSlot) {
		return this.actFrameStartTime + ((freeSlot * SLOT_DISTANCE) + SLOT_HALF_SIZE);
	}
}
